#include "SitamapLocalPath.h"

#include "DataBase_Session.h"
#include <iostream>
#include <stdio.h>
#include <QThread>
#include <QString>
#include <qtconcurrentrun.h>
#include <QApplication>
#include <windows.h>
#include <QXmlStreamWriter>


using namespace QtConcurrent;
using namespace std;




PopoonLocalPath::PopoonLocalPath( const  QString mapfile )
  :fi(mapfile),started(false),pipeloop(0),generator_strip_root(false),xmlinstream(true),cleaned_end(false)
{
    qDebug() << "### PopoonLocalPath construct map -> " << fi.fileName();
    qDebug() << "QThread PopoonLocalPath  from-> " << QThread::currentThread();
}

void PopoonLocalPath::processing()
{
    qDebug() << "### PopoonLocalPath processing map -> " << fi.fileName();
    qDebug() << "QThread PopoonLocalPath  from-> " << QThread::currentThread();
    
    if (fi.exists()) {
    QTimer::singleShot(5, this, SLOT(read()));
    } else {
     emit terminate();
    }
}

void PopoonLocalPath::loadPipe( const QDomElement e )
{
    if ( e.tagName() != QString::fromLatin1("map:pipeline") )
          return;
    pipeloop++;
    DataBase_Session *sx = DataBase_Session::instance();
    /* test if pipe can fast process or need moore params */
    if (!testrunPipe(e)) {
    sx->PipeList.insert(pipeloop,e); 
    }
    
}

bool PopoonLocalPath::setConfig( const QString dir  )
{
   ficonf = QFileInfo(dir+"/osconfig.xml");
   StreamBuf *buf = new StreamBuf();
        if (buf->writefull(utf_8config()))
              buf->PutOnFile(dir+"/osconfig.xml");           
        
    emit handle_msg("### OS config on osconfig.xml " + dir + "/");
    delete buf;
    return true;
}

bool PopoonLocalPath::testrunPipe( const QDomElement e  )
{
    qDebug() << "### testrunPipe init   -> " << e.tagName();
    
    Cxml.clear();
    xmlinstream = true;
    
    if ( e.tagName() != QString::fromLatin1("map:pipeline") )
          return false;
    QDomElement match = e.firstChildElement(QString::fromLatin1("map:match"));
    if (match.attribute(QString::fromLatin1("type")) == "file") {
        const QString wathis = match.attribute(QString::fromLatin1("pattern"));
        if (wathis.contains("*") || wathis.isEmpty() ) {
             emit error_msg(QString("Need more parameters or  Empty pipeline on xml file %1").arg(fi.absoluteFilePath()),false);
             return false; 
        }
        QFileInfo xfi(wathis);
        Cache(xfi.absolutePath());
        setConfig(xfi.absolutePath());
        const QString xmltempfile = xfi.absolutePath() + "/" + xfi.baseName() + "_tmp.xml";
        emit handle_msg("### Pipe file create -> " + xfi.absoluteFilePath() + ".");
        
        qt_unlink(xfi.absoluteFilePath());
        
        QmyDom pdom;  /* start a new xml stream */
        QDomElement nroot = pdom.createElement("root");
        pdom.appendChild(nroot);
        QDomElement lasttag;
        QDomNode child = match.firstChild();
           while ( !child.isNull() )
            {
                if ( child.isElement() )
                {
                    const QDomElement el = child.toElement();
                    if ( el.tagName() == QString::fromLatin1("map:aggregate") ) {
                        const QString tagname = el.attribute(QString::fromLatin1("element"));
                        if (!tagname.isEmpty()) {
                            if (lasttag.isNull())  {
                                nroot.setTagName(tagname);
                                lasttag = nroot;
                            } else {
                                lasttag = pdom.createElement(tagname);
                            }
                            QDomElement parter = el.firstChildElement ("map:part");
                            if (!parter.isNull()) {
                                 setPart(parter);
                                 createGenerators(parter.firstChild(),lasttag);
                            }
                            
                            
                            
                        }
                    } else if ( el.tagName() == QString::fromLatin1("map:part") ) {
                       setPart(el);
                       createGenerators(el.firstChild(),lasttag);
                    }  else if ( el.tagName() == QString::fromLatin1("map:generate") ) {
                        generator_strip_root = true;
                        createGenerators(match.firstChild(),nroot);
                    }
                    
                }
                
                child = child.nextSibling();
            }
        
        /////pdom.Print();
        if (xmlinstream) {
        Cxml = pdom.toByteArray(2);  /*  last xml stream report */
        }
            
        StreamBuf *buf = new StreamBuf();
        StreamBuf *reader = new StreamBuf();
        StreamBuf *writteln = new StreamBuf();
        reader->device()->write(Cxml); 
        reader->start();
        reader->PutOnFile(xmltempfile);
        reader->start();
            

            
        ///////QFileInfo xfi(wathis);
        QDateTime timer1( QDateTime::currentDateTime() );
            
            
    
        
        QDomElement transform = match.firstChildElement("map:transform");
        QDomElement serialize = match.firstChildElement("map:serialize");
        QDomElement diraction = match.firstChildElement("map:convert");
            
        if (!transform.isNull()) {
            const QString tipes = transform.attribute(QString::fromLatin1("type"));
            const QString src = transform.attribute(QString::fromLatin1("src"));
            QFileInfo xsltfile(src);
            if (tipes == "xslt2") {
            emit handle_msg("### Call XSLT20 file -> " + xsltfile.absoluteFilePath());
            QXmlQuery xquery2(QXmlQuery::XSLT20);
            xquery2.setFocus(reader->device());
            xquery2.bindVariable("unixtime",QVariant(timer1.toTime_t()));
            appendParams(transform.firstChild(),&xquery2);
            xquery2.setQuery(QUrl(xsltfile.absoluteFilePath()));
            xquery2.evaluateTo ( buf->device() );
            Cxml = buf->stream();    /*  last xml stream report */
            } else if (tipes == "xslt1") {
            emit handle_msg("### Call XSLT20 file -> " + xsltfile.absoluteFilePath());
            QXmlQuery xquery1(QXmlQuery::XQuery10);
            xquery1.setFocus(reader->device());
            xquery1.bindVariable("unixtime",QVariant(timer1.toTime_t()));
            appendParams(transform.firstChild(),&xquery1);
            xquery1.setQuery(QUrl(xsltfile.absoluteFilePath()));
            xquery1.evaluateTo ( buf->device() );
            Cxml = buf->stream(); /*  last xml stream report */
            } else if (tipes == "xmlsqlite2") {
                QString tablename = allowName(transform.attribute(QString::fromLatin1("src")));
                if (tablename.isEmpty())  {
                    tablename = allowName(xfi.baseName());
                }
                QByteArray tmp = Cxml;
                emit handle_msg("### try xml to sqlite2 file dump as tablename -> " + tablename);
                writteln->savechunk(xfi.absoluteFilePath(),Cxml);
                StreamBuf *bsql = new StreamBuf();
                QFuture<QByteArray> f1 = run(xmlsqlite2Dump,xfi.absoluteFilePath(),tablename);
                f1.waitForFinished();
                const QByteArray sqlinserts = f1.result();
                bsql->savechunk(xfi.absolutePath() + "/" + tablename + "_gen.sql",sqlinserts);
                delete bsql;
                Cxml = tmp;
            }
        
        }
        
        if (!serialize.isNull()) {
            const QString tipes = serialize.attribute(QString::fromLatin1("type"));
            const QString cleantmp = serialize.attribute(QString::fromLatin1("clean"));
            
            if (tipes == "xml" || tipes == "text") {
                writteln->savechunk(xfi.absoluteFilePath(),Cxml);
            } else if (tipes == "fo2pdf") {
                QString pdfname = serialize.attribute(QString::fromLatin1("src"));
                if (pdfname.isEmpty())
                       pdfname = xfi.absolutePath() + "/" + xfi.baseName() + ".pdf";
                
                if (writteln->savechunk(xfi.absoluteFilePath(),Cxml)) {
                    emit handle_msg("### fop processing file -> " + xfi.absoluteFilePath());
                    QString cmd = fo2pdfserialize(xfi.absoluteFilePath(),pdfname);
                    if (cmd.size() > 0) 
                        emit handle_msg(cmd);
                    if (cleantmp == "yes") {
                      qt_unlink(xfi.absoluteFilePath());  
                    }
                    
                }
            } else if ( tipes == "xmlaccess") {
               writteln->savechunk(xfi.absoluteFilePath(),Cxml);
            }  else {
               writteln->savechunk(xfi.absoluteFilePath(),Cxml);
            }
            if (cleantmp == "yes") {
            qt_unlink(xmltempfile);
            cleaned_end = true;
            }
        }
        
        
        delete writteln;
        delete buf;
        delete reader;
            
            
    }
    
    
    
   return true;
}


void PopoonLocalPath::appendParams( QDomNode params , QXmlQuery * xsltquery  )
{
    
    
      while ( !params.isNull() )
            {
                if ( params.isElement() ) {
                    const QDomElement par = params.toElement();
                    if ( par.tagName() == "map:parameter" ) {
                        const QString xname = par.attribute(QString::fromLatin1("name"));
                        if (!xname.isEmpty()) {
                            QVariant xx = param_eval(par.attribute(QString::fromLatin1("value")));
                            xsltquery->bindVariable(xname,xx);
                        } 
                    }
                }
                params = params.nextSibling();
            }
            
      
}


QVariant PopoonLocalPath::param_eval( const QString value )
{
    QVariant pending;
    if (value.startsWith("constant(")) {
        pending = QString("constant_fail");
    } else {
      pending = value;
    }
    return pending;
}

void PopoonLocalPath::setPart( const QDomElement e )
{
    if ( e.tagName() == QString::fromLatin1("map:part")) {
        
                     if (e.attribute(QString::fromLatin1("strip-root")) == "true" ) {
                          generator_strip_root = true;
                      } else {
                          generator_strip_root = false; /* reset */
                      }
        
    }
    
}


void PopoonLocalPath::createGenerators( QDomNode source , QDomElement append  )
{
    qDebug() << "### PopoonLocalPath::createGenerators   " << source.nodeName();
    
    
    
           while ( !source.isNull() )
            {
                if ( source.isElement() ) {
                    const QDomElement gen = source.toElement();
                    //////qDebug() << "### stream   " << gen.tagName();
                    /////////qDebug() << "### type   " << gen.attribute("type");
                    
                    if ( gen.tagName() == "map:generate" ) {
                    const QString src = gen.attribute(QString::fromLatin1("src"));
                    const QString dsn = gen.attribute(QString::fromLatin1("dsn"));
                    const QString striproot = gen.attribute(QString::fromLatin1("strip-root"));
                    if (!striproot.isEmpty()) {
                        if (striproot == "true" )
                             generator_strip_root = true;
                        if (striproot == "false" )
                             generator_strip_root = false;
                    }
                        
                    /*  database generator */
                    if (gen.attribute("type") == "db2xml" && 
                               !src.isEmpty() &&
                               !dsn.isEmpty() ) {
                        emit handle_msg("### Call db query -> " + src + " on " + dsn + " db connection name.");
                        QByteArray stream = xmlutf_8Query(src,dsn);
                        if (stream.size() > 0) {                                   
                        importOnCurrentNode(stream,append);     
                        }                            
                    } else if (gen.attribute("type") == "db2csv" && !src.isEmpty() && !dsn.isEmpty() ) {
                        xmlinstream = false;
                        Cxml = textQuery(src,dsn,txtCSV);
                    }  else if (gen.attribute("type") == "db2sql" && !src.isEmpty() && !dsn.isEmpty() ) {
                        xmlinstream = false;
                        Cxml = textQuery(src,dsn,txtSQLITE);
                    }   else if (gen.attribute("type") == "db2excelxml" && !src.isEmpty() && !dsn.isEmpty() ) {
                        xmlinstream = false;
                        Cxml = xmlexcel2008(src,dsn);
                    }  else if (gen.attribute("type") == "file" && !src.isEmpty() ) {
                    /*  from file  generator */
                        QFileInfo gfi(src);
                        if (gfi.exists()) { 
                            QFile file(gfi.absoluteFilePath()); 
                            if (file.open(QFile::ReadOnly | QFile::Text)) {
                            importOnCurrentNode(file.readAll(),append);  
                            file.close();
                            }
                        } else {
                         emit error_msg(QString("Unable to open xml file %1").arg(gfi.absoluteFilePath()),false);
                        }
                    }
                    
                   }
                }
                
                
                source = source.nextSibling();
            }
}


void PopoonLocalPath::onclose()
{
    qDebug() << "PopoonLocalPath::onclose  call  from-> " << QThread::currentThread();
    emit handle_msg("### PopoonLocalPath shedule to delete objekt.");
    deleteLater();
}


void PopoonLocalPath::importOnCurrentNode( const QByteArray xml , QDomElement append )
{
        QDomDocument idom;
        if (!idom.setContent(xml,false,0,0,0)) {
          return;
        }
        
        
        qDebug() << "import stream on node  from-> " << QThread::currentThread();
        
        QDomElement iroot = idom.documentElement();
        QDomNamedNodeMap alist = iroot.attributes(); 
        
        QDomElement emulateroot;
         if (!generator_strip_root) {
             emulateroot = append.ownerDocument().createElement(iroot.tagName());
             for (int i=0; i<alist.count(); i++){
			    QDomNode nod = alist.item(i);
			    emulateroot.setAttribute(nod.nodeName().toLower(),nod.nodeValue());
			 }  
             append.appendChild(emulateroot);
         }
         QDomNode child = iroot.firstChild();
					while ( !child.isNull() ) {
						if ( child.isElement() ) {
                            if (generator_strip_root) {
					        append.appendChild(append.ownerDocument().importNode(child,true).toElement());
                            } else {
                            emulateroot.appendChild(append.ownerDocument().importNode(child,true).toElement());
                            }
					    }
				     child = child.nextSibling();            
				   }
}










void PopoonLocalPath::read()
{
    qDebug() << "### PopoonLocalPath read map -> " << fi.fileName();
    if (started) {
     return;   /* one way parser delete to parse next file  */
    }
    
    qDebug() << "### read started   -> " << started;
    started = true;
    qDebug() << "### read started   -> " << started;
    
    QmyDom *dom = new QmyDom();
    if (!dom->SetSelf( fi.absoluteFilePath() )) {
        emit error_msg(QString("Unable to load xml file %1").arg(fi.absoluteFilePath()),false);
        emit terminate();
        return;
    }
    
    QDomElement root = dom->firstChildElement("map:sitemap");
    if (root.isNull()) {
        emit error_msg(QString("No root tag map:sitemap found on xml file %1").arg(fi.absoluteFilePath()),false);
        emit terminate();
        return;
    }
    
    QDomNode child = root.firstChild();

    
    while ( !child.isNull() )
    {
        if ( child.isElement() )
        {
            const QDomElement el = child.toElement();
            
            
            if ( el.tagName() == QString::fromLatin1("map:components") ) {
              loadComponents( el );
            }
            if ( el.tagName() == QString::fromLatin1("map:pipeline") ) {
              loadPipe( el );
            }
            
        }
        
        child = child.nextSibling();
    }
    
    
    
    if (cleaned_end && ficonf.exists()) 
           qt_unlink(ficonf.absoluteFilePath());
    
    emit terminate();
    qDebug() << "### PopoonLocalPath end map -> " << fi.fileName();
    qDebug() << "QThread PopoonLocalPath  from-> " << QThread::currentThread();
}




void PopoonLocalPath::loadComponents( const QDomElement e )
{
    qDebug() << "### loadComponents init   -> " << e.tagName();
    
    if ( e.tagName() != QString::fromLatin1("map:components") )
          return;
    QDomNode child = e.firstChild();
    DataBase_Session *sx = DataBase_Session::instance();
    
    while ( !child.isNull() )
    {
        if ( child.isElement() )
        {
            const QDomElement el = child.toElement();
            if ( el.tagName() == QString::fromLatin1("map:scheme") ) {
               const QString name = el.attribute(QString::fromLatin1("name"));
               if (name == "dbconnect") {
                   if (el.attribute(QString::fromLatin1("type")) == "yes")
                       sx->append(el.attribute(QString::fromLatin1("path")));
                   
               } else if (name == "cd") {
                     const QString pathlocale = el.attribute(QString::fromLatin1("path"));
                     QDir localdir(pathlocale);
                     if (Cache(localdir.absolutePath())) {
                        QDir::setCurrent( localdir.absolutePath() ); 
                     } else {
                         QDir::setCurrent( pathlocale ); 
                     }
               }
            }
            
        }
        
        child = child.nextSibling();
    }
}















